/**
 * Project Name:pay-protocol
 * File Name:Xml.java
 * Package Name:cn.swiftpass.pay.protocol
 * Date:2014-8-10下午10:48:21
 *
*/

package com.lcc.bank.tools.util;

import cn.hutool.core.util.StrUtil;
import com.fingard.gardpay.common.constant.MsgFieldConstant;
import org.dom4j.*;
import org.dom4j.io.SAXReader;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import java.io.StringReader;
import java.util.*;

/**
 *
 *
 * @version v1.0 2018/7/10
 * @author chenlw
 *
 */
public class XmlUtil {
	/**
	 * 解析document（禁用DTDs）
	 *
	 * @param text
	 *            xml字符串
	 * @return Document对象
	 * @throws DocumentException
	 *             DocumentException
	 * @throws SAXException
	 *             SAXException
	 * @author chenlw v1.0 2018/7/10
	 * @see DocumentHelper#parseText
	 */
	public static Document parseText(String text) throws DocumentException, SAXException {
		Document result;

		SAXReader reader = new SAXReader();
		// 禁用DTDs
		reader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
		String encoding = getEncoding(text);

		InputSource source = new InputSource(new StringReader(text));
		source.setEncoding(encoding);

		result = reader.read(source);

		// if the XML parser doesn't provide a way to retrieve the encoding,
		// specify it manually
		if (result.getXMLEncoding() == null) {
			result.setXMLEncoding(encoding);
		}

		return result;
	}

	private static String getEncoding(String text) {
        String result = "UTF-8";

		String xml = text.trim();

		String xmlEnd = "<?xml";

		if (xml.startsWith(xmlEnd)) {
			int end = xml.indexOf("?>");
			String sub = xml.substring(0, end);
			StringTokenizer tokens = new StringTokenizer(sub, " =\"\'");

			while (tokens.hasMoreTokens()) {
				String token = tokens.nextToken();

				if ("encoding".equals(token)) {
					if (tokens.hasMoreTokens()) {
						result = tokens.nextToken();
					}

					break;
				}
			}
		}

		return result;
	}

	/**
	 * xml转map 不带属性
	 *
	 * @param xmlStr
	 * @param needRootKey
	 *            是否需要在返回的map里加根节点键
	 * @return
	 * @throws DocumentException
	 * @throws SAXException
	 */
	public static Map<String, Object> xml2map(String xmlStr, boolean needRootKey) throws DocumentException, SAXException {
		Document doc = parseText(xmlStr);
		Element root = doc.getRootElement();
		Map<String, Object> map = (Map<String, Object>) xml2map(root);
		if (root.elements().size() == 0 && root.attributes().size() == 0) {
			return map;
		}
		if (needRootKey) {
			// 在返回的map里加根节点键（如果需要）
			Map<String, Object> rootMap = new HashMap<String, Object>();
			rootMap.put(root.getName(), map);
			return rootMap;
		}
		return map;
	}

	/**
	 * xml转map 不带属性
	 *
	 * @param e
	 * @return
	 */
	@SuppressWarnings("unchecked")
    public static Map<String, Object> xml2map(Element e) {
		Map<String, Object> map = new LinkedHashMap<String, Object>();

		List<Element> list = e.elements();
		if (list.size() > 0) {
			for (int i = 0; i < list.size(); i++) {
				Element iter = list.get(i);
				List<Object> mapList = new ArrayList<Object>();

				if (iter.elements().size() > 0) {
					Map<String, Object> m = xml2map(iter);
					if (map.get(iter.getName()) != null) {
						Object obj = map.get(iter.getName());
						if (!(obj instanceof List)) {
							mapList = new ArrayList<Object>();
							mapList.add(obj);
							mapList.add(m);
						}
						if (obj instanceof List) {
							mapList = (List<Object>) obj;
							mapList.add(m);
						}
						map.put(iter.getName(), mapList);
					} else {
						map.put(iter.getName(), m);
					}
				} else {
					if (map.get(iter.getName()) != null) {
						Object obj = map.get(iter.getName());
						if (!(obj instanceof List)) {
							mapList = new ArrayList<Object>();
							mapList.add(obj);
							mapList.add(iter.getText());
						}
						if (obj instanceof List) {
							mapList = (List<Object>) obj;
							mapList.add(iter.getText());
						}
						map.put(iter.getName(), mapList);
					} else{
						map.put(iter.getName(), iter.getText());
					}
				}
			}
		} else{
			map.put(e.getName(), e.getText());
		}
		return map;
	}

	/**
	 * xml转map 带属性
	 *
	 * @param xmlStr
	 * @param needRootKey
	 *            是否需要在返回的map里加根节点键
	 * @return
	 * @throws DocumentException
	 * @throws SAXException
	 */
	public static Map<String, Object> xml2mapWithAttr(String xmlStr, boolean needRootKey) throws DocumentException, SAXException {
		Document doc = parseText(xmlStr);
		Element root = doc.getRootElement();
		Map<String, Object> map = xml2mapWithAttr(root);
		// 根节点只有一个文本内容
		if (root.elements().size() == 0 && root.attributes().size() == 0) {
			return map;
		}
		// 在返回的map里加根节点键（如果需要）
		if (needRootKey) {
			Map<String, Object> rootMap = new HashMap<String, Object>();
			rootMap.put(root.getName(), map);
			return rootMap;
		}
		return map;
	}

	/**
	 * xml转map 带属性
	 *
	 * @param e
	 * @return
	 */
	@SuppressWarnings("unchecked")
	private static Map<String, Object> xml2mapWithAttr(Element element) {
		Map<String, Object> map = new LinkedHashMap<String, Object>();

		List<Element> list = element.elements();
		// 当前节点的所有属性的list
		List<Attribute> listAttr0 = element.attributes();
		for (Attribute attr : listAttr0) {
			map.put("@" + attr.getName(), attr.getValue());
		}
		if (list.size() > 0) {

			for (int i = 0; i < list.size(); i++) {
				Element iter = list.get(i);
				List<Object> mapList = new ArrayList<Object>();

				if (iter.elements().size() > 0) {
					Map<String, Object> m = xml2mapWithAttr(iter);
					if (map.get(iter.getName()) != null) {
						Object obj = map.get(iter.getName());
						if (!(obj instanceof List)) {
							mapList = new ArrayList<Object>();
							mapList.add(obj);
							mapList.add(m);
						}
						if (obj instanceof List) {
							mapList = (List<Object>) obj;
							mapList.add(m);
						}
						map.put(iter.getName(), mapList);
					} else{
						map.put(iter.getName(), m);
					}
				} else {
					// 当前节点的所有属性的list
					List<Attribute> listAttr = iter.attributes();
					Map<String, Object> attrMap = null;
					boolean hasAttributes = false;
					if (listAttr.size() > 0) {
						hasAttributes = true;
						attrMap = new LinkedHashMap<String, Object>();
						for (Attribute attr : listAttr) {
							attrMap.put("@" + attr.getName(), attr.getValue());
						}
					}

					if (map.get(iter.getName()) != null) {
						Object obj = map.get(iter.getName());
						if (!(obj instanceof List)) {
							mapList = new ArrayList<Object>();
							mapList.add(obj);
							// mapList.add(iter.getText());
							if (hasAttributes) {
								attrMap.put("#text", iter.getText());
								mapList.add(attrMap);
							} else {
								mapList.add(iter.getText());
							}
						}
						if (obj instanceof List) {
							mapList = (List<Object>) obj;
							// mapList.add(iter.getText());
							if (hasAttributes) {
								attrMap.put("#text", iter.getText());
								mapList.add(attrMap);
							} else {
								mapList.add(iter.getText());
							}
						}
						map.put(iter.getName(), mapList);
					} else {
						// map.put(iter.getName(), iter.getText());
						if (hasAttributes) {
							attrMap.put("#text", iter.getText());
							map.put(iter.getName(), attrMap);
						} else {
							map.put(iter.getName(), iter.getText());
						}
					}
				}
			}
		} else {
			// 根节点的
			if (listAttr0.size() > 0) {
				map.put("#text", element.getText());
			} else {
				map.put(element.getName(), element.getText());
			}
		}
		return map;
	}

	public static String getNodeTextNeedExists(Document doc, String pName, String name) throws Exception{

		String nodeName = pName + (pName.endsWith(MsgFieldConstant.SPLIT_STR) ? "" : MsgFieldConstant.SPLIT_STR) + name;
		Node node = doc.selectSingleNode(nodeName);
		if(node == null){
			throw new Exception("节点" + pName + name + "不存在");
		}

		return StrUtil.trimToEmpty(node.getText());
	}

	public static String getNodeText(Document doc, String pName, String name) throws Exception{

		String nodeName = pName + (pName.endsWith(MsgFieldConstant.SPLIT_STR) ? "" : MsgFieldConstant.SPLIT_STR) + name;
		Node node = doc.selectSingleNode(nodeName);
		if(node == null){
			return StrUtil.EMPTY;
		}

		return StrUtil.trimToEmpty(node.getText());
	}
}
